package scatter.order.rest.service.impl;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import scatter.common.rest.exception.BusinessException;
import scatter.order.rest.service.ICreateOrderFrameworkService;

/**
 * <p>
 * 抽象创建订单服务
 * </p>
 *
 * @author yangwei
 * @since 2021-06-27 14:14
 */
@Slf4j
@Transactional
public abstract class AbstractCreateOrderFrameworkServiceImpl<T> implements ICreateOrderFrameworkService<T> {

	/**
	 * 创建订单之前调用，主要用来校验参数等
	 * @param param
	 * @return
	 */
	protected boolean preCreateOrder(T param){
		return true;
	}


	/**
	 * 判断订单是否存在，如果订单已存在，需要返回已存在的订单号，如果订单不存在则返回空
	 * 后面会继续创建订单
	 * @param param
	 * @return
	 */
	protected String existOrder(T param){
		return null;
	}

	/**
	 * 扣减库存
	 * @param param
	 * @return
	 */
	protected boolean deductInventory(T param){

		return true;
	}

	/**
	 * 回滚库存
	 * @param param
	 * @return
	 */
	protected boolean rollbackInventory(T param){

		return true;
	}

	@Transactional
	@Override
	public String createOrder(T param) {
		log.info("创建订单开始，参数为param = {}",toJsonStr(param));

		// 订单前参数查检
		boolean preCreateOrder = preCreateOrder(param);
		if (!preCreateOrder) {
			throw new BusinessException("创建订单失败，创建订单前处理返回 false");
		}
		String existOrderNo = existOrder(param);
		if(StrUtil.isNotEmpty(existOrderNo)){
			log.info("订单已存在直接返回 existOrderNo={}",existOrderNo);
			return existOrderNo;
		}


		// 扣减库存
		boolean deductInventory = deductInventory(param);
		if (!deductInventory) {
			throw new BusinessException("创建订单失败，库存不足");
		}
		// 创建订单
		String orderNo  = null;
		// 创建订单是否成功
		boolean isCreateOrderSuccess = false;
		try {
			orderNo = doCreateOrder(param);
			isCreateOrderSuccess = true;
		}catch (BusinessException e){
			log.error("创建订单失败，捕获了业务异常,直接抛出",e);
			throw e;
		}catch (Exception e){
			log.error("创建订单失败，捕获了全局异常，直接抛出",e);
			throw e;
		}
		finally {
			// 如果订单没有创建成功，需要回滚库存
			if (!isCreateOrderSuccess) {
				try {
					boolean rollbackInventory = rollbackInventory(param);
					if (!rollbackInventory) {
						throw new BusinessException("创建订单回滚库存失败，因回滚库存返回 false");
					}
				}catch (Exception e){
					log.error("创建订单回滚库存失败，因回滚库存抛出异常，记录一下日志",e);
					throw e;
				}
			}
		}
		// 检查订单号是否为空
		if (isStrEmpty(orderNo)) {
			// 运行到这里可能是有bug，但运行到这里时理论上库存已经回滚，直接抛出异常就可以了
			throw new BusinessException("创建订单失败，订单号为空，未知原因");
		}

		// 后续处理,这里如果处理失败了，注意问题
		// 1. 如果是在单体服务中，可以抛出异常来回滚所有数据，但在微服务中抛出异常也无法改变异步系统已经提交的数据
		try {
			postCreateOrder(param, orderNo);
		} catch (Exception e) {
			log.error("创建订单后处理失败",e);
			boolean postCreateOrderFailThrowException = isPostCreateOrderFailThrowException();
			if (postCreateOrderFailThrowException) {
				log.info("创建订单后处理失败允许抛出异常");
				throw e;
			}
		}

		return orderNo;
	}

	/**
	 * 实际创建订单
	 * @param param
	 * @return 返回订单号
	 */
	protected abstract String doCreateOrder(T param);

	/**
	 * 创建订单后调用，主要用来处理其它业务
	 * @param param
	 * @param orderNo
	 * @return 返回true代表成功
	 */
	protected void postCreateOrder(T param,String orderNo){
	}

	/**
	 * 是否在创建订单后处理失败，抛出异常
	 * 这在单机模式很有用，会回滚所有数据，如果是在微服务中，建议返回false，毕竟订单已经成功创建，补好数据就行
	 * @return
	 */
	protected boolean isPostCreateOrderFailThrowException(){
		return true;
	}
}
